package com.nekonomics
package persistence.repository

import domain.auxiliary.SimpleCartItem
import domain.detail.CartItemDetail
import domain.entity.{CartItem, Sku}
import utils.SqlUtil
import utils.magic.SqlMagic

import cats.data.*
import doobie.*
import doobie.implicits.*
import doobie.postgres.implicits.*
import zio.*


trait SkuRepo {

  def pageTotalSize: ConnectionIO[Long]


  def getBySkuId(skuId: Long): ConnectionIO[Sku]
  
  def findBySkuId(skuId: Long): ConnectionIO[Option[Sku]]

  def findStockBySkuId(skuId: Long): ConnectionIO[Option[Int]]
  
  def listByIds(skuIds: List[Long]): ConnectionIO[List[Sku]]

  def reserveStock(items: List[SimpleCartItem]): ConnectionIO[Int]
  
  def reserveStockCI(items: List[CartItem]): ConnectionIO[Int] = reserveStock(items.map(SimpleCartItem.of))

  def reserveStockCID(items: List[CartItemDetail]): ConnectionIO[Int] = reserveStock(items.map(SimpleCartItem.of))
}


object SkuRepo {
  val live: ULayer[SkuRepo] = ZLayer.succeed(new SkuRepoLive)
}


class SkuRepoLive extends SkuRepo {

  override def findBySkuId(skuId: Long): ConnectionIO[Option[Sku]] = {
    SqlUtil.selectFromWhereOpt[Sku](Some(fr"id = $skuId"))
      .query[Sku]
      .option
  }

  override def getBySkuId(skuId: Long): ConnectionIO[Sku] = {
    SqlUtil.selectFromWhereOpt[Sku](Some(fr"id = $skuId"))
      .query[Sku]
      .unique
  }

  

  override def pageTotalSize: ConnectionIO[Long] = {
    sql"select count(id) from public.sku"
      .query[Long]
      .unique
  }


  override def findStockBySkuId(skuId: Long): ConnectionIO[Option[Int]] = {
    sql"""
         select stock from public.sku
         where id = $skuId
       """
      .query[Int]
      .option
  }

 


  override def listByIds(skuIds: List[Long]): ConnectionIO[List[Sku]] = {
    val frIn = Fragments.in(fr"id", NonEmptyList(skuIds.head, skuIds.tail))
    SqlUtil.selectFromWhereOpt[Sku](Some(frIn))
      .query[Sku]
      .to[List]
  }


  // todo 后续研究怎么加锁减库存才合适
  override def reserveStock(items: List[SimpleCartItem]): ConnectionIO[Int] = {
    val placeholders = items.map{case SimpleCartItem(id, qty) => s"($id, $qty)"}.mkString(",")
    val sql =
      s"""
         |UPDATE public.sku AS s
         |SET stock = s.stock - v.qty
         |FROM (values $placeholders ) AS v(sku_id, qty)
         |WHERE s.id = v.sku_id AND s.stock >= v.qty
         |""".stripMargin
    Update0(sql, None).run
  }


}
